﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Flagwind.Data
{
	/// <summary>
	/// 实体类构造程序，提供一个接口，自动创建实体类。
	/// </summary>
	public class EntityBuilder
	{
		#region 私有字段

		private readonly Type _targetType;
		private readonly static Dictionary<Type, Type> _entityTypes = new Dictionary<Type, Type>();
		private readonly static object _lock = new object();

		#endregion

		#region 构造方法

		/// <summary>
		/// 构造函数
		/// </summary>
		/// <param name="targetType">被实现或者继承的类型</param>
		public EntityBuilder(Type targetType)
		{
			this._targetType = targetType;
		}

		#endregion

		#region 静态方法

		/// <summary>
		/// 注册实体类的具体实现类。
		/// </summary>
		/// <param name="interfaceType">接口类型。</param>
		/// <param name="instanceType">实例类型。</param>
		public static void RegisterType(Type interfaceType, Type instanceType)
		{
			if(!instanceType.IsSubclassOf(typeof(EntityBase)))
			{
				throw new ArithmeticException(instanceType.Name + " 必须是 IEntity 的实现类！");
			}
			if(instanceType.GetInterface(interfaceType.Name) != interfaceType)
			{
				throw new ArithmeticException(instanceType.Name + " 必须是 " + interfaceType.Name + " 的实现类！");
			}
			_entityTypes.Add(interfaceType, instanceType);
		}

		/// <summary>
		/// 根据接口类型，创建实体类的实例。
		/// </summary>
		/// <typeparam name="T">接口类型。</typeparam>
		/// <returns>类型实例。</returns>
		public static T CreateEntity<T>() where T : class
		{
			Type targetType = null;
			Type sourceType = typeof(T);
			if(sourceType.IsSubclassOf(typeof(EntityBase)) || sourceType.IsSubclassOf(typeof(IDataReadable)) || !sourceType.IsInterface)
			{
				return Activator.CreateInstance<T>();
			}
			else
			{
				//如果在类型字典里面没有找到接口的类型实现，则自动创建一个实体类
				if(!_entityTypes.TryGetValue(sourceType, out targetType))
				{
					lock(_lock)
					{
						if(!_entityTypes.TryGetValue(sourceType, out targetType))
						{
							var builder = new EntityBuilder(sourceType);
							targetType = builder.Build();
							_entityTypes[sourceType] = targetType;
						}
					}
				}
			}
			T entity = (T)Activator.CreateInstance(targetType);
			return entity;
		}

		#endregion

		#region 实例方法

		/// <summary>
		/// 构造实体类。
		/// </summary>
		/// <returns>实体类型。</returns>
		public Type Build()
		{
			var appDomain = AppDomain.CurrentDomain;
			var assemblyName = new AssemblyName();
			// 为要创建的 Assembly 定义一个名称
			assemblyName.Name = "Flagwind_Assembly_" + _targetType.Name;
			var assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
			var moduleBuilder = assemblyBuilder.DefineDynamicModule("Flagwind_Method_" + _targetType.Name);
			var newTypeName = "Flagwind_DynamicEntity_" + _targetType.Name;
			// 新类型的属性：要创建的是 Class，而非 Interface，Abstract Class等，而且是 Public的
			var newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public;
			//声明要创建的新类型的父类型
			Type newTypeParent;
			//声明要创建的新类型要实现的接口
			Type[] newTypeInterfaces;
			//对于基类型是否为接口，作不同处理
			if(_targetType.IsInterface)
			{
				newTypeParent = typeof(EntityBase);
				newTypeInterfaces = new Type[]
				{
					_targetType
				};
			}
			else
			{
				newTypeParent = _targetType;
				newTypeInterfaces = new Type[0];
			}
			//得到类型生成器            
			var typeBuilder = moduleBuilder.DefineType(newTypeName, newTypeAttribute, newTypeParent, newTypeInterfaces);
			typeBuilder.AddInterfaceImplementation(_targetType);
			//定义构造函数
			BuildConstructor(typeBuilder, newTypeParent, _targetType.Name);
			//以下将为新类型声明方法：新类型应该 override 基类型的所以 virtual 方法
			PropertyInfo[] pis = _targetType.GetProperties();
			List<string> propertyNames = new List<string>();
			foreach(PropertyInfo pi in pis)
			{
				propertyNames.Add(pi.Name);
				//属性构造器
				PropertyBuilder propBuilder = typeBuilder.DefineProperty(pi.Name, System.Reflection.PropertyAttributes.HasDefault, pi.PropertyType, null);
				MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final;
				//构造Get访问器
				MethodBuilder getPropMethodBuilder = typeBuilder.DefineMethod("Get_" + pi.Name, getSetAttr, pi.PropertyType, Type.EmptyTypes);
				BuildGeter(pi.Name, newTypeParent, pi.PropertyType, getPropMethodBuilder);
				//构造Set访问器
				MethodBuilder setPropMethodBuilder = typeBuilder.DefineMethod("Set_" + pi.Name, getSetAttr, null, new Type[]
				{
					pi.PropertyType
				});
				BuildSeter(pi.Name, newTypeParent, pi.PropertyType, setPropMethodBuilder);
				//添加到属性构造器
				propBuilder.SetGetMethod(getPropMethodBuilder);
				propBuilder.SetSetMethod(setPropMethodBuilder);
			}
			MethodBuilder SetFieldNamesBuilder = typeBuilder.DefineMethod("SetFieldNames", MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig);
			BuildSetFieldNames(newTypeParent, SetFieldNamesBuilder, propertyNames.ToArray());
			//真正创建，并返回
			Type resuleType = typeBuilder.CreateType();
			//assyBuilder.Save(assyName.Name+".dll");
			return resuleType;
		}

		/// <summary>
		/// 构造构造函数。
		/// </summary>
		/// <param name="typeBuilder"></param>
		/// <param name="newTypeParent"></param>
		/// <param name="newTypeName"></param>
		private void BuildConstructor(TypeBuilder typeBuilder, Type newTypeParent, string newTypeName)
		{
			//去除接口名称的约定 I 字母打头，作为表名称
			string tableName = newTypeName.Length > 1 ? newTypeName.Substring(1) : newTypeName;
			ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.HasThis, null);
			ILGenerator ctorIL = constructorBuilder.GetILGenerator();
			ctorIL.Emit(OpCodes.Ldarg_0);
			ctorIL.Emit(OpCodes.Call, newTypeParent.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)[0]);
			ctorIL.Emit(OpCodes.Ldarg_0);
			ctorIL.Emit(OpCodes.Ldstr, tableName);
			ctorIL.Emit(OpCodes.Call, newTypeParent.GetMethod("Set_TableName", BindingFlags.NonPublic | BindingFlags.Instance));
			ctorIL.Emit(OpCodes.Ret);
		}

		/// <summary>
		/// 生成 SetFieldNames 方法
		/// </summary>
		/// <param name="baseType"></param>
		/// <param name="methodBuilder"></param>
		/// <param name="names"></param>
		private void BuildSetFieldNames(Type baseType, MethodBuilder methodBuilder, string[] names)
		{
			string str = string.Join(",", names);
			MethodInfo splitMI = typeof(string).GetMethod("Split", new Type[]
			{
				typeof(char[])
			});
			MethodInfo set_PropertyNamesMI = baseType.GetMethod("Set_PropertyNames", BindingFlags.Instance | BindingFlags.NonPublic);
			var ilGenerator = methodBuilder.GetILGenerator();
			var charArray = ilGenerator.DeclareLocal(typeof(char[]));
			ilGenerator.Emit(OpCodes.Ldarg_0);
			ilGenerator.Emit(OpCodes.Ldstr, str);
			ilGenerator.Emit(OpCodes.Ldc_I4_1);
			ilGenerator.Emit(OpCodes.Newarr, typeof(char));
			ilGenerator.Emit(OpCodes.Stloc_0);
			ilGenerator.Emit(OpCodes.Ldloc_0);
			ilGenerator.Emit(OpCodes.Ldc_I4_0);
			ilGenerator.Emit(OpCodes.Ldc_I4_S, 0x2c);
			ilGenerator.Emit(OpCodes.Stelem_I2);
			ilGenerator.Emit(OpCodes.Ldloc_0);
			ilGenerator.Emit(OpCodes.Callvirt, splitMI);
			ilGenerator.Emit(OpCodes.Callvirt, set_PropertyNamesMI);
			ilGenerator.Emit(OpCodes.Ret);
		}

		/// <summary>
		/// 构造 Get 访问器。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <param name="baseType">父类。</param>
		/// <param name="propertyType">属性类型。</param>
		/// <param name="methodBuilder">方法生成器。</param>
		private void BuildGeter(string propertyName, Type baseType, Type propertyType, MethodBuilder methodBuilder)
		{
			MethodInfo getProperty = null;
			MethodInfo[] methodInfos = typeof(EntityBase).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
			foreach(MethodInfo info in methodInfos)
			{
				if(info.Name == "GetFieldValue" && info.IsGenericMethod)
				{
					getProperty = info;
					break;
				}
			}
			getProperty = getProperty.MakeGenericMethod(propertyType);
			var generator = methodBuilder.GetILGenerator();
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldstr, propertyName);
			generator.Emit(OpCodes.Call, getProperty);
			generator.Emit(OpCodes.Ret);
		}

		/// <summary>
		/// 构造 Set 访问器。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <param name="baseType">父类。</param>
		/// <param name="propertyType">属性类型。</param>
		/// <param name="methodBuilder">方法生成器。</param>
		private void BuildSeter(string propertyName, Type baseType, Type propertyType, MethodBuilder methodBuilder)
		{
			MethodInfo setProperty = null;
			var methodInfos = typeof(EntityBase).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
			foreach(MethodInfo info in methodInfos)
			{
				if(info.Name == "SetFieldValue")
				{
					if(info.GetParameters().Length == 2)
					{
						setProperty = info;
						break;
					}
				}
			}
			var generator = methodBuilder.GetILGenerator();
			generator.Emit(OpCodes.Ldarg_0);
			generator.Emit(OpCodes.Ldstr, propertyName);
			generator.Emit(OpCodes.Ldarg_1);
			//是否是值类型
			if(propertyType.IsValueType)
			{
				generator.Emit(OpCodes.Box, propertyType);
			}
			generator.Emit(OpCodes.Call, setProperty);
			generator.Emit(OpCodes.Ret);
		}
	}

	#endregion
}